use std::path::{Path, PathBuf};
use anyhow::Result;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize, de::DeserializeOwned};

use crate::{
    utils::{
        const_assert::{ConstAssert, IsTrue}, floor_div_i32, floor_mod_i32, shape::RuntimeShape3
    },
    voxel::{
        chunk::Chunk,
        layout::{chunk_voxel_count, region_chunk_count},
        mesh::Mesh,
        region::Region,
    },
};

// ------------------------------------------------------------
// 基础 ID 定义
// ------------------------------------------------------------
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
pub struct RegionId(pub i32, pub i32, pub i32);

#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
pub struct ChunkId(pub u32, pub u32, pub u32);

#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
pub struct TileId(pub u32, pub u32, pub u32);

// ------------------------------------------------------------
// World
// ------------------------------------------------------------
pub struct World<
    const RBITS: u8,
    const CBITS: u8,
    DATA: Clone + Serialize + DeserializeOwned + Default,
> where
    [(); region_chunk_count::<RBITS>()]:,
    [(); chunk_voxel_count::<CBITS>()]:,
    ConstAssert<{ RBITS <= 8 }>: IsTrue,
    ConstAssert<{ CBITS <= 8 }>: IsTrue,
{
    regions: FxHashMap<RegionId, Region<RBITS, CBITS, DATA>>,
    base_dir: PathBuf,
}

impl<const RBITS: u8, const CBITS: u8, DATA: Clone + Serialize + DeserializeOwned + Default>
    World<RBITS, CBITS, DATA>
where
    [(); region_chunk_count::<RBITS>()]:,
    [(); chunk_voxel_count::<CBITS>()]:,
    ConstAssert<{ RBITS <= 8 }>: IsTrue,
    ConstAssert<{ CBITS <= 8 }>: IsTrue,
{
    pub fn new(base_dir: &Path) -> Result<Self> {
        Ok(Self {
            regions: FxHashMap::default(),
            base_dir: base_dir.to_path_buf(),
        })
    }

    pub fn get_region_mut(&mut self, id: RegionId) -> Option<&mut Region<RBITS, CBITS, DATA>> {
        if !self.regions.contains_key(&id) {
            let region = Region::new(id, &self.base_dir).ok()?;
            self.regions.insert(id, region);
        }
        self.regions.get_mut(&id)
    }

    pub fn get_region_ref(&self, id: RegionId) -> Option<&Region<RBITS, CBITS, DATA>> {
        self.regions.get(&id)
    }

    /// 统一后的 locate:
    /// 给世界体素坐标 (x,y,z)，返回所属的 region / chunk / tile 局部坐标
    #[inline]
    pub fn locate(&self, x: i32, y: i32, z: i32) -> LocateResult {
        let csize = 1i32 << CBITS;  // chunk 体素边长，比如 32
        let rsize = 1i32 << RBITS;  // region 按 chunk 计的一边多少chunk，比如 32

        // 全局chunk坐标
        let cx = floor_div_i32(x, csize);
        let cy = floor_div_i32(y, csize);
        let cz = floor_div_i32(z, csize);

        // tile在chunk内
        let tx = floor_mod_i32(x, csize) as u32;
        let ty = floor_mod_i32(y, csize) as u32;
        let tz = floor_mod_i32(z, csize) as u32;

        // region坐标（按chunk分组再下取整）
        let rix = floor_div_i32(cx, rsize);
        let riy = floor_div_i32(cy, rsize);
        let riz = floor_div_i32(cz, rsize);

        // chunk在region内 [0,R)
        let clx = floor_mod_i32(cx, rsize) as u32;
        let cly = floor_mod_i32(cy, rsize) as u32;
        let clz = floor_mod_i32(cz, rsize) as u32;

        LocateResult {
            region_id: RegionId(rix, riy, riz),
            chunk_local: ChunkId(clx, cly, clz),
            tile_local: TileId(tx, ty, tz),
        }
    }

    /// 给定 region_id + 这个 region 内的 chunk 局部坐标，算出这个 chunk 的世界体素原点
    #[inline]
    pub fn chunk_world_origin_from_region_local(
        &self,
        region_id: RegionId,
        clx: u32,
        cly: u32,
        clz: u32,
    ) -> (i32, i32, i32) {
        // region_id 是 region 的全局 index，单位是 chunk
        let rx0_chunk = region_id.0 << RBITS;
        let ry0_chunk = region_id.1 << RBITS;
        let rz0_chunk = region_id.2 << RBITS;

        // 全局 chunk 坐标
        let cx_global = rx0_chunk + (clx as i32);
        let cy_global = ry0_chunk + (cly as i32);
        let cz_global = rz0_chunk + (clz as i32);

        // 转成体素原点
        let wx0 = cx_global << CBITS;
        let wy0 = cy_global << CBITS;
        let wz0 = cz_global << CBITS;
        (wx0, wy0, wz0)
    }

    /// 内部小工具：尝试拿某个具体 chunk（全局chunk坐标 cx,cy,cz 对应的 region/chunk_local）
    /// 返回 Option<&Chunk<...>>
    #[inline]
    fn get_chunk_ref_global_chunk(
        &self,
        cx: i32,
        cy: i32,
        cz: i32,
    ) -> Option<&Chunk<CBITS, DATA>> {
        let rsize = 1i32 << RBITS;

        let rix = floor_div_i32(cx, rsize);
        let riy = floor_div_i32(cy, rsize);
        let riz = floor_div_i32(cz, rsize);

        let clx = floor_mod_i32(cx, rsize) as u32;
        let cly = floor_mod_i32(cy, rsize) as u32;
        let clz = floor_mod_i32(cz, rsize) as u32;

        let region_id = RegionId(rix, riy, riz);
        let region = self.regions.get(&region_id)?;
        region.get_chunk_ref(clx, cly, clz)
    }

    pub fn meshify_chunk(&self, wx0: i32, wy0: i32, wz0: i32) -> Result<Mesh> {
        let c_vox: i32 = 1 << CBITS;    // chunk内部真实体素边长，比如32
        let h_vox: i32 = c_vox + 2;     // halo大小，比如34
    
        // 1. 定位当前chunk的全局chunk坐标
        let cx0 = floor_div_i32(wx0, c_vox);
        let cy0 = floor_div_i32(wy0, c_vox);
        let cz0 = floor_div_i32(wz0, c_vox);
    
        // 2. 缓存 3x3x3 邻居引用，避免每个采样都 locate() 哈希
        let mut neighbors = [[[None; 3]; 3]; 3];
        for dz in 0..3 {
            for dy in 0..3 {
                for dx in 0..3 {
                    let ncx = cx0 + (dx as i32 - 1);
                    let ncy = cy0 + (dy as i32 - 1);
                    let ncz = cz0 + (dz as i32 - 1);
                    neighbors[dz][dy][dx] = self.get_chunk_ref_global_chunk(ncx, ncy, ncz);
                }
            }
        }
    
        // 如果中心块本体都没加载那就没网格
        let Some(center_chunk) = neighbors[1][1][1] else {
            return Ok(Mesh {
                positions: Vec::new(),
                normals:   Vec::new(),
                indices:   Vec::new(),
            });
        };
    
        // 3. 构建 (C+2)^3 的 SDF field
        // 注意：field[i] 的“坐标”语义等同于 fast-surface-nets 里的本地格点坐标
        // 索引0其实代表 "chunk边界外的一层padding"
        let dim_h = h_vox as usize;
        let dim_h2 = dim_h * dim_h;
        let total = dim_h * dim_h * dim_h;
        let mut field = vec![0.0f32; total];
    
        // 我们要把世界体素坐标 [wx0-1 .. wx0+c_vox] 采样进 [0 .. h_vox)
        let start_x = wx0 - 1;
        let start_y = wy0 - 1;
        let start_z = wz0 - 1;
    
        let sample_density = |wx: i32, wy: i32, wz: i32| -> f32 {
            // 找它属于哪个 chunk
            let cx = floor_div_i32(wx, c_vox);
            let cy = floor_div_i32(wy, c_vox);
            let cz = floor_div_i32(wz, c_vox);
    
            // 局部坐标
            let lx = floor_mod_i32(wx, c_vox) as u32;
            let ly = floor_mod_i32(wy, c_vox) as u32;
            let lz = floor_mod_i32(wz, c_vox) as u32;
    
            // 映射到 neighbors[dz][dy][dx]
            let rx = (cx - cx0) + 1; // 0..2
            let ry = (cy - cy0) + 1;
            let rz = (cz - cz0) + 1;
    
            if (0..3).contains(&rx) && (0..3).contains(&ry) && (0..3).contains(&rz) {
                if let Some(ch) = neighbors[rz as usize][ry as usize][rx as usize] {
                    return ch.get_density(lx, ly, lz);
                }
            }
    
            // 邻居没加载？fallback：用中心chunk边缘clamp
            let clamped_x = wx.clamp(wx0, wx0 + c_vox - 1);
            let clamped_y = wy.clamp(wy0, wy0 + c_vox - 1);
            let clamped_z = wz.clamp(wz0, wz0 + c_vox - 1);
    
            let lx2 = (clamped_x - wx0) as u32;
            let ly2 = (clamped_y - wy0) as u32;
            let lz2 = (clamped_z - wz0) as u32;
    
            center_chunk.get_density(lx2, ly2, lz2)
        };
    
        for lz in 0..h_vox {
            let base_z = (lz as usize) * dim_h2;
            let wz = start_z + lz;
            for ly in 0..h_vox {
                let base_yz = base_z + (ly as usize) * dim_h;
                let wy = start_y + ly;
                for lx in 0..h_vox {
                    let wx = start_x + lx;
                    field[base_yz + lx as usize] = sample_density(wx, wy, wz);
                }
            }
        }
    
        // 4. 跑 surface_nets 覆盖整个 [0 ..= H-1] 立方体
        //    这正是 docs 的用法：min=[0,0,0], max=[H-1,H-1,H-1]
        let dim_h_u32 = h_vox as u32;
        let min = [0u32, 0u32, 0u32];
        let max = [dim_h_u32 - 1, dim_h_u32 - 1, dim_h_u32 - 1];
    
        let mut sn_buf = fast_surface_nets::SurfaceNetsBuffer::default();
        fast_surface_nets::surface_nets(&field, &RuntimeShape3::new(dim_h_u32), min, max, &mut sn_buf);
    
        // 5. 平移坐标到世界
        //    文档说：只需要把网格平移到chunk所在的位置就可以了
        //    → offset = (wx0, wy0, wz0)   注意！不是 wx0-1 !!!
        let off_x = wx0 as f32;
        let off_y = wy0 as f32;
        let off_z = wz0 as f32;
    
        for p in &mut sn_buf.positions {
            p[0] += off_x;
            p[1] += off_y;
            p[2] += off_z;
        }
    
        // 6. 法线归一化（fast-surface-nets 已经算了法线，但防守式再normalize一下）
        for n in &mut sn_buf.normals {
            let len2 = n[0]*n[0] + n[1]*n[1] + n[2]*n[2];
            if len2 > 0.0 {
                let len = len2.sqrt();
                n[0] /= len;
                n[1] /= len;
                n[2] /= len;
            } else {
                *n = [0.0, 1.0, 0.0];
            }
        }
    
        Ok(Mesh {
            positions: sn_buf.positions,
            normals:   sn_buf.normals,
            indices:   sn_buf.indices,
        })
    }
    

}

// ------------------------------------------------------------
// 结果结构体
// ------------------------------------------------------------
#[derive(Debug, Clone)]
pub struct LocateResult {
    pub region_id: RegionId,   // 哪个 region
    pub chunk_local: ChunkId,  // 这个 region 里哪个 chunk [0,R)
    pub tile_local:  TileId,   // 这个 chunk 里哪个体素 [0,C)
}
